/************************************************************************
 * voipd by Markus Weissbacher weissbacher at gmail dot com		*
 * 									*
 * This program is Free Software, released under the GNU General	*
 * Public License v2.0 http://www.gnu.org/licenses/gpl 			*
 *									*
 * This program is an voip (sip) client to be an example of how		*
 * a sip client can work. It also aims for being a minimal voip		*
 * client allowing a easy portation and adaption to anybody's		*
 * needs, with the least possible overhead.				*
 *									*
 * This program requires:						*
 * libosip								*
 * libeXosip								*
 * libortp								*
 * and maybe speex in the future as it is an awesome codec		*
 *									*
 ************************************************************************/

// includes

#include		<ctype.h>
#include		<syslog.h>
#include		<pthread.h>
#include		<signal.h>

#include		<eXosip2/eXosip.h>

#define PROG_NAME 	"voipd"
#define PROG_VER  	"1.0"
#define UA_STRING 	"voipd v" PROG_VER

#include		<ortp/ortp.h>
#include		<ortp/stun.h>
#include		<ortp/telephonyevents.h>
#include		<osip2/osip.h>
#include		<sys/types.h>
#include		<sys/socket.h>
#include		<netinet/in.h>
#include		<netdb.h>
#include		<stdlib.h>
//defines

#ifdef _SPEEX_
	//not implemented for now
	#undef _SPEEX_
#endif

//structs
	//struct to be given to the lease refresh thread
	typedef struct regparam_t
	{
		int regid;
		int expiry;
		int auth;
	}regparam_t;

	//this struct is supposed to hold all data for sip stuff
	typedef struct sip_struct
	{
		eXosip_event_t *event;
		int event_in_use;
		char *stun_addr;
		StunAddress4 remote;
		StunAddress4 local;
		int dtmf_supported;
		int audio_payloadtype;
	}sip_struct;

	//struct to hold all incoming connection sockets
	typedef struct client_list_struct
	{
		int *sock_addr;
		int num;
		int maintenance;
	}client_list_struct;

//global variable(s)

	int cond=1;
	client_list_struct client_list;
	FILE* sendfile=NULL;

//function definitions
	extern int parse_configfile(char **stunstr, char **port, char **proxy, char **username, char **password, char* configfilepath);
	//thread to reregister if expiry has passed
	static void *refresh_sip_lease (void *arg);
	//thread to fork if one call comes in to handle stun and rtp stuff
	static void *incoming_call (void* arg);

	//function to handle incoming sdp requests and handle all nat traversal stuff
	sip_struct *sdp(int did, osip_message_t *answer, StunAddress4 mapped);

	//rtp is splited to send/receive for debuging purpose and to be easily readable
	//receive an rtp stream
	static void *rtp_recv(void* arg);
	static void *rtp_send(void* arg);
	static void *socket_handler(void* arg);
	static void* socket_send(client_list_struct* arg, char* message);
	//send an rtp stream
	//static void *rtp_send(void* arg);

	//function to pares an UInt32 ip to an char* ip
	char *iptostr(UInt32 addr);
	//handle incoming dtmf traffic
	void receive_telephone_events(RtpSession *session,int type,long user_data);


int main(int argc, char *argv[])
{
	/*variables*/
	char	*contact = NULL, *fromuser=NULL, *stunstr=NULL, *port=NULL, *proxy=NULL, *username=NULL, *password=NULL;
	struct	regparam_t regparam = { 0, 1200, 0 };
	struct	osip_thread *register_thread;
	int	nofork=1;
	char	*configfilepath="config";
	FILE	*configfile;

	/*set client list to empty*/
	client_list.sock_addr=NULL;
	client_list.num=0;
	client_list.maintenance=0;

	/*initialise ortp, reinitialising it for each call seems to let it crash*/
	ortp_scheduler_init();
	ortp_init();

	/*parse input parameters*/
	if(argc>1)
	{
		if(strstr(argv[1], "--configfile="))
		{
			configfilepath=(argv[1]+strlen("--configfile="));
		}
		else
		{
			fprintf(stderr, "only --configfile= parameter can be accepted\n");
			exit (1);
		}
	}
	/*configfile parser*/
	if(0!=parse_configfile(&stunstr, &port, &proxy, &username, &password, configfilepath))
	{
		fprintf(stderr, "failed to open configfile\n");
		exit(1);
	}
	/*parse the "fromuser" argument needed for sip*/
	if(username && proxy)
	{
		fromuser=strdup("sip:");
		fromuser=realloc(fromuser, 5+(strstr(proxy, "sip:")?-4:0)+strlen(proxy)+strlen(username));
		strcat(fromuser, username);
		strcat(fromuser, "@\0");
		strcat(fromuser, (strstr(proxy, "sip:")?4:0)+proxy);
	}

	/*quit if no proxy or no fromuser is specified*/
	if (!proxy || !fromuser)
	{
		syslog (LOG_ERR, "no proxy, or username specified\n");
		fprintf(stderr, "no proxy, or username specified\n");
		exit (1);
	}

	if(NULL == port)
		port=strdup("65000");

	/*dont demonize if nofork is specified*/
	if (!nofork)
	{
		int cpid = fork ();

		if (cpid)                 // parent 
//			exit (0);
		// child 
		close (0);
		close (1);
		close (2);
	} 

	/*eXosip initialisation*/
	if (eXosip_init ())
	{
		syslog (LOG_ERR, "eXosip_init failed");
		fprintf(stderr, "eXosip_init failed\n");
		exit (1);
	}
	
	if (eXosip_listen_addr (IPPROTO_UDP, NULL, atoi(port), AF_INET, 0))
	{
		syslog (LOG_ERR, "eXosip_listen_addr failed");
		fprintf(stderr, "eXosip_listen_addr failed\n");
		exit (1);
	}

	eXosip_set_user_agent (UA_STRING);
	if (username && password)
	{
		syslog (LOG_INFO, "username: %s", username);
		syslog (LOG_INFO, "password: [removed]");
		if (eXosip_add_authentication_info(username, username, password, NULL, NULL))
		{
			syslog (LOG_ERR, "eXosip_add_authentication_info failed");
			fprintf(stderr, "eXosip_add_authentication_info failed\n");
			exit (1);
		}
		free(password);
		free(username);
	}


	/*register on the sip proxy*/
	{
		osip_message_t *reg = NULL;
		int i;
		fprintf(stderr, "fromuser : %s\nproxy : %s\ncontact : %s\nregparam.expiry*2 : %i \n", fromuser, proxy, contact, regparam.expiry*2);
		regparam.regid = eXosip_register_build_initial_register (fromuser, proxy, contact, regparam.expiry*2, &reg);
		if (regparam.regid < 1)
		{
			syslog (LOG_ERR, "eXosip_register_build_initial_register failed");
//			exit (1);
		}
		i = eXosip_register_send_register (regparam.regid, reg);
		if (i != 0)
		{
			syslog (LOG_ERR, "eXosip_register_send_register failed");
//			exit (1);
		}
	}
	/*open a thread to handle reregistering all the time*/
	register_thread=(struct osip_thread*)osip_thread_create(5012, refresh_sip_lease, &regparam);
	if (register_thread==NULL)
	{
		syslog (LOG_ERR, "pthread_create failed");
//		exit (1);
	}

	sip_struct *infos;
	infos=malloc(sizeof(sip_struct));
	{
		char *temp=NULL;
		StunAddress4 stun_addr, mapped, localip, changed;
		int i;

		/*check out local ip*/
		temp=malloc(128);
		eXosip_guess_localip (AF_INET, temp, 128);
		stunParseServerName(temp, &localip);
		printf("localip is %s\n", temp);
		free(temp);
		infos->local.port=5012;

		/*nat detection, if stun server given*/
		/*
		if(stunstr && stunParseServerName(stunstr, &stun_addr))
		{
			i=stunNatType(&stun_addr, 0, 0, 0, NULL);
			switch (i)
			{
				case 1:
				{*/
					syslog (LOG_INFO, "No Nat detected");
					infos->local.addr=localip.addr;/*
					break;
				}
				case 2: 
				case 3:
				case 4:
				case 5:
				case 6:
				{
					syslog (LOG_INFO, "Nat detected, masquerading");
					i=stunTest(&stun_addr, 1, &localip, &mapped, &changed);
					infos->local.addr=mapped.addr;
					syslog (LOG_INFO, "mapped.addr is %s", iptostr(mapped.addr));
					eXosip_masquerade_contact (iptostr(mapped.addr), localip.port);
					break;
				}
				case 0:
				case 7:
				case 8:
				{
					syslog (LOG_ERR, "Nat detection failure");
				}
			}
		}
		else
		{
			infos->local.addr=localip.addr;
		}
		*/
	}

	{
		int free;
		infos->event_in_use=0;
		/*open a thread to handle incoming voipd<->voipdc* connections*/
		register_thread=(struct osip_thread*)osip_thread_create(5012, socket_handler, &client_list);
		if (register_thread==NULL)
		{
			syslog (LOG_ERR, "pthread_create failed");
//			exit (1);
		}
		/*exosip main event loop*/
		while(1)
		{
			/*stop new events from being polled as long other functions require them*/
			if(infos->event_in_use)
			{
				osip_usleep (100);
				continue;
			}
			if (!(infos->event = eXosip_event_wait (0, 1)))
			{
				osip_usleep (100);
				continue;
			}
		
			eXosip_automatic_action ();
			free=1;
		
			/*switch over event types*/
			switch (infos->event->type)
			{
			
				case EXOSIP_REGISTRATION_NEW:
					syslog (LOG_INFO, "received new registration");
					break;
				case EXOSIP_REGISTRATION_SUCCESS:
					syslog (LOG_INFO, "registered successfully");
					break;
				case EXOSIP_REGISTRATION_FAILURE:
					syslog (LOG_INFO, "registration failed");
					regparam.auth = 1;
					break;
				case EXOSIP_REGISTRATION_TERMINATED:
					syslog (LOG_INFO, "registration failed");
					break;
				case EXOSIP_CALL_CLOSED:
					syslog (LOG_INFO, "call terminated");
					cond=0;
					break;
				case EXOSIP_CALL_INVITE:
					syslog (LOG_INFO, "incoming call");
					infos->stun_addr=stunstr;
					infos->event_in_use=1;
					register_thread=(struct osip_thread*)osip_thread_create (5012, incoming_call, infos);
					if (register_thread==NULL)
					{
						syslog (LOG_ERR, "pthread_create failed");
//						exit (1);
					}
					/*reserve the signal for the function incoming_call until the function has finished working on it*/
					free=0; 
					break;
					/*handler to stop annoying OPTION requests from asterisk*/
				case EXOSIP_MESSAGE_NEW:
					syslog (LOG_INFO, "message received");
					eXosip_options_send_answer(infos->event->tid, 200, NULL);
					break;
				default: 
					syslog (LOG_DEBUG,
						"recieved eXosip event in default (type, did, cid) = (%d, %d, %d) with textinfo %s",
						infos->event->type, infos->event->did, infos->event->cid, infos->event->textinfo);
			} 
			if(free)
			{
				eXosip_event_free (infos->event);
			}
		}
	}
}


static void *incoming_call (void* arg)
{
	int i;
//	char localip[128];
	struct osip_thread *register_thread;
	sip_struct *remote_rtp;
	osip_message_t *answer = NULL;
	sip_struct *infos=(sip_struct*)arg;
	eXosip_event_t *event=malloc(sizeof(*infos->event));
	memcpy(event, infos->event, sizeof(*infos->event));
	infos->event_in_use=0;
	cond=1;				
	eXosip_lock ();
//	eXosip_guess_localip (AF_INET, localip, 128);
	/*send ringing*/
	eXosip_call_send_answer (event->tid, 180, NULL);

	if(NULL!=event->ack)syslog (LOG_DEBUG, "reason: ack: %s", event->ack->message);
	/*send ok for call*/
	i = eXosip_call_build_answer (event->tid, 200, &answer);
	if (i != 0)
	{
		eXosip_call_send_answer(event->tid, 400, NULL);
	}
	else
	{
		syslog (LOG_DEBUG, "stun addr is %s\n", infos->stun_addr);
		/*do all sdp stuff and return rtp relevant stuff*/
		remote_rtp=sdp(event->did, answer, infos->local);
		eXosip_call_send_answer (event->tid, 200, answer);

/*		register_thread=(struct osip_thread*) osip_thread_create (20000, rtp_send, remote_rtp);
		if (register_thread==NULL)
		{
			syslog (LOG_ERR, "pthread_create failed");
			exit (1);
		}
*/
		/*start receiving rtp packets*/
		register_thread=(struct osip_thread*)osip_thread_create (5015, rtp_recv, remote_rtp);
		if (register_thread==NULL)
		{
			syslog (LOG_ERR, "pthread_create failed");
//			exit (1);
		}
		
		eXosip_unlock ();

	}
	return NULL;

}

/*function to do all sdp stuff (what codec, what ips ...)*/
sip_struct *sdp(int did, osip_message_t *answer, StunAddress4 mapped)
{
	sdp_message_t *remote_sdp;
	sdp_media_t *remote_med;
	int pos=-1, media_number, match=0;
	char *buffer;
	char *attributes;
	char *temp=NULL;
	sip_struct *infos;
	StunAddress4	localip;

	StunAddress4 destination;

	infos=malloc(sizeof(sip_struct));
	buffer=malloc(sizeof(char)*4097);
	attributes=malloc(sizeof(char)*500);
	attributes[0]='\0';

	infos->audio_payloadtype=-1;
	infos->dtmf_supported=0;
	
	/*check out local ip*/
  	temp=malloc(128);
	eXosip_guess_localip (AF_INET, temp, 128);
	stunParseServerName(temp, &localip);
	printf("localip is %s\n", temp);
	free(temp);
	localip.port=5012;
/*	*/

	/*retrieve the sdp information sent by the caller*/
	remote_sdp=eXosip_get_remote_sdp(did);
	if (remote_sdp == NULL)
	{
		OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "No remote SDP body found for call\n"));
		return NULL;                // no existing body? 
	}
	remote_med=eXosip_get_audio_media(remote_sdp);

	stunParseServerName(remote_sdp->c_connection->c_addr, &destination);
	destination.port=atoi(remote_med->m_port);

	/*nat detection, if stun server given
	if(stunstr && stunParseServerName(stunstr, &stun_addr))
	{
		i=stunNatType(&stun_addr, 0, 0, 0, 0, NULL);
		switch (i)
		{
			case 1:
			{
			*/
				syslog (LOG_INFO, "No Nat detected");
				mapped.addr=localip.addr;
				/*
				break;
			}
			case 2: 
			case 3:
			case 4:
			case 5:
			case 6:
			{
				syslog (LOG_INFO, "Nat detected, masquerading");
				i=stunTest(&stun_addr, 1, 1, &localip, &mapped, &changed);
				localip.port=5012;
				eXosip_masquerade_contact (iptostr(mapped.addr), localip.port);
				break;
			}
			case 0:
			case 7:
			case 8:
			{
				syslog (LOG_ERR, "Nat detection failure");
			}
		}
	}
	else
	{
		mapped.addr=localip.addr;
	}
	*/
	/*add default initial header to buffer*/
	temp=iptostr(mapped.addr);
	snprintf (buffer, 4096,
		"v=0\r\n"
		"o=marcellus 0 0 IN IP4 %s\r\n"
		"s=conversation\r\n"
		"c=IN IP4 %s\r\n"
		"t=0 0\r\n"
		"m=audio %d RTP/AVP", temp, temp, mapped.port);
	free(temp);

	/*check for matching codecs*/
	while (!osip_list_eol (&remote_med->m_payloads, ++pos))
	{
		match=1;
		media_number=atoi((char*)osip_list_get(&remote_med->m_payloads, pos));
		switch (media_number)
		{
			case 0: //PCMU
				syslog (LOG_INFO,	"PCMU support detected");
				strcat(attributes,	"a=rtpmap:0 PCMU/8000\r\n");
				if(infos->audio_payloadtype<0)infos->audio_payloadtype=0;
				break;
			case 8: //PCMA
				syslog (LOG_INFO,	"PCMA support detected");
				strcat(attributes,	"a=rtpmap:8 PCMA/8000\r\n");
				if(infos->audio_payloadtype<0)infos->audio_payloadtype=8;
				break;

			case 101: //telephone events
				syslog (LOG_INFO,	"telephone events support detected");
				strcat(attributes, 	"a=rtpmap:101 telephone-event/8000\r\n"
							"a=fmtp:101 0-15\r\n");
				infos->dtmf_supported=1;
				break;
			#ifdef _SPEEX_
			case 114: //speex support to be implemented
				syslog (LOG_INFO,	"SPEEX support detected");
				strcat(attributes,	"a=rtpmap:110 SPEEX/8000\r\n");
				if(infos->audio_payloadtype<0)infos->audio_payloadtype=114;
				break;
			#endif
			default:
				match=0;
				syslog (LOG_INFO, "unsupported codec nr:%s detected", (char*)osip_list_get(&remote_med->m_payloads, pos));
		}
		/*append supported media number to media string*/
		if(match)sprintf((buffer+strlen(buffer)*sizeof(char)), " %d", media_number);
	}
	sprintf((buffer+strlen(buffer)*sizeof(char)), 	"\r\n"
							"%s", attributes);
	temp=iptostr(destination.addr);
	syslog (LOG_DEBUG, "destination port number is %d, ip is %s", destination.port, temp);
	free(temp);
//	infos->local.addr=localip.addr;
//	infos->local.port=localip.port;
	infos->remote.addr=destination.addr;
	infos->remote.port=destination.port;
	osip_message_set_body (answer, buffer, strlen (buffer));
	osip_message_set_content_type (answer, "application/sdp");
	sdp_message_free (remote_sdp);
	free(attributes);
	free(buffer);
	return infos;
}

/*dtmf audio handling (work in progress*/
static void *rtp_recv(void *arg)
{

	struct osip_thread *register_thread;
	sip_struct *infos=arg;
	RtpSession *session;
	unsigned char buffer[160];
	int err;
	uint32_t ts=0;
	FILE *outfile;
	char* temp=NULL;
	int have_more;

	outfile=fopen("output.wav" ,"wb");
	if (outfile==NULL) 
	{
		syslog (LOG_DEBUG, "Cannot open file for writing");
		return NULL;
	}
	
//	ortp_set_log_level_mask(ORTP_DEBUG|ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);
//	ortp_set_log_level_mask(ORTP_DEBUG|ORTP_WARNING|ORTP_ERROR);
	/*add the supported audio codecs to the av_profile*/
	if(infos->dtmf_supported)rtp_profile_set_payload(&av_profile,101,&payload_type_telephone_event);	
	if(0==infos->audio_payloadtype)rtp_profile_set_payload(&av_profile,0,&payload_type_pcmu8000);	
	if(8==infos->audio_payloadtype)rtp_profile_set_payload(&av_profile,0,&payload_type_pcma8000);	
	#ifdef _SPEEX_
		if(114==infos->audio_payloadtype)rtp_profile_set_payload(&av_profile,0,&payload_type_speex_nb);	
	#endif

	/*initialise the sessions for audio*/
	session=rtp_session_new(RTP_SESSION_SENDRECV);

	/*configure the session for audio*/
	rtp_session_set_scheduling_mode(session,1);
	rtp_session_set_blocking_mode(session,1);
	rtp_session_set_local_addr(session, "0.0.0.0", infos->local.port);
	temp=iptostr(infos->remote.addr);
	rtp_session_set_remote_addr(session, temp, infos->remote.port);
	free(temp);
	rtp_session_enable_rtcp(session, FALSE);
	rtp_session_set_payload_type(session,infos->audio_payloadtype);

	/*configure the session for dtmf*/
	if(infos->dtmf_supported)
	{
		/*intitialsise the dtmf session*/
		rtp_session_signal_connect(session,"telephone-event",(RtpCallback)receive_telephone_events,0);
	}
	else
	{
		syslog (LOG_DEBUG, "no dtmf support detected, no control possible :(");
	}
	//intialize send file
	sendfile=fopen("send.wav" ,"r");
        if (sendfile==NULL) 
	{
		syslog (LOG_ERR, "coudn't open send file, disabling send");
        }
	register_thread=(struct osip_thread*)osip_thread_create (5015, rtp_send, session);
	if (register_thread==NULL)
	{
		syslog (LOG_ERR, "pthread_create failed");
//		exit (1);
	}


	rtp_session_set_symmetric_rtp(session,TRUE);
	rtp_session_set_connected_mode(session,TRUE);
	rtp_session_enable_jitter_buffer(session, TRUE);
//	rtp_session_enable_adaptive_jitter_compensation(session,adapt);
//	rtp_session_set_jitter_compensation(session,jittcomp);

	while(cond)
	{
		have_more=1;
		/*receive part, but this won't work as long it is bound to sending*/
		while (have_more)
		{
			int stream_received=0;
			err=rtp_session_recv_with_ts(session,buffer,160,ts,&have_more);
			if (err>0 || stream_received) //stream_received=1;
			{
				stream_received=1;
				fwrite(buffer,1,err,outfile);
			}
//			if(payloadtype!=rtp_session_get_recv_payload_type(session))
//			printf("payloadtype is %d\n", payloadtype=rtp_session_get_recv_payload_type(session));
		}
		ts+=160;
	}
	fclose(outfile);	

	if(infos->dtmf_supported)
	{
		rtp_session_signal_disconnect_by_callback(session,"telephone-event", (RtpCallback)receive_telephone_events);
	}
	free(infos);
	osip_thread_join(register_thread);
	rtp_session_destroy(session);
	return 0;
}

/*rtp sending seperated from receiving because its nessecary for asynchronous send/receive*/
static void *rtp_send(void* arg)
{
	RtpSession 	*session=arg;
	int		i;
	unsigned char 	sendbuffer[160];
	uint32_t 	ts=0;

	/*send something*/
	while(cond && (i=fread(sendbuffer, 1, 160, sendfile))>0)
	{
		rtp_session_send_with_ts(session, sendbuffer, i, ts);
		ts+=160;
	}
	fclose(sendfile);
	osip_thread_exit();
	return NULL;
}

/*translate UInt32 ip adresses to strings, make sure to free them after use*/
char *iptostr(UInt32 addr)
{
	char *string=malloc(16);
	snprintf(string, 16, "%d.%d.%d.%d", (unsigned int)(addr/16777216), (unsigned int)(addr/65536)&255, (unsigned int)((addr/256)&255), (unsigned int)(addr&255));
	return string;
}

/*refresh sip lease*/
static void *refresh_sip_lease (void *arg)
{
	struct regparam_t *regparam = arg;
	int reg;

	while(1)
	{
		sleep (regparam->expiry / 2);
		eXosip_lock ();
		reg = eXosip_register_send_register (regparam->regid, NULL);
		if (0 > reg)
		{
			perror ("eXosip_register");
//			exit (1);
		}
		regparam->auth = 0;
		eXosip_unlock ();
	}
	return NULL;
}

/*socket handler to accept all incoming connections and save their sockets, so the sending part can send to all sockets*/
static void *socket_handler(void* arg)
{
	struct sockaddr_in address;
	int addresssize=sizeof(struct sockaddr_in);
	int serversocket;

	address.sin_addr.s_addr=INADDR_ANY;
	address.sin_port=htons(20000);
	address.sin_family=AF_INET;

	(client_list.sock_addr)=NULL;
	client_list.num=-1;

	/*create an TCP socket*/
	serversocket=socket(AF_INET,SOCK_STREAM,0);
	if(SOCKET_ERROR==serversocket)
	{
		printf("Could not make a socket\n");
//		exit(0);
	}

	/*bind the socket to port 20000*/
	if(SOCKET_ERROR==bind(serversocket, (struct sockaddr*)&address, sizeof(address)))
	{
		printf("Couldn't connect to host\n");
//		exit(0);
	}
	/*listen to the port*/
	if(SOCKET_ERROR == listen(serversocket, 5))
	{
		printf("Couldn't listen\n");
//		exit(0);
	}
	/*wait for incoming connections and add their sockets to the socket list*/
	while(1)
	{
		if(client_list.maintenance)
		{
			sleep(1);
			continue;
		}
		(client_list.sock_addr)=realloc((client_list.sock_addr), (1+(++(client_list.num)))*sizeof(int));
		client_list.sock_addr[client_list.num]=accept(serversocket, (struct sockaddr*)&address, (socklen_t*)&addresssize);
	}
	
}

/*print all incoming telephone events out to socket_send*/
void receive_telephone_events(RtpSession *session,int type,long user_data)
{
	char buffer[100];
	sprintf(buffer, "received telephone event nr \"%2d\"\n", type);
	printf("received telephone event by callback nr \"%2d\"\n", type);
	socket_send(NULL, buffer);
	return;
}

/*send message to all open sockets*/
static void* socket_send(client_list_struct* arg, char* message)
{
	int i=0;
	signal(SIGPIPE, SIG_IGN);
	for(i=0;i<client_list.num;i++)
	{
		if(0>=write(client_list.sock_addr[i], message, (strlen(message)+1)*sizeof(char)))
		{
			printf("writing %s to clients\n", message);
			client_list.maintenance=1;
			close(client_list.sock_addr[i]);
			memmove((client_list.sock_addr+i), (client_list.sock_addr+i+1), (client_list.num-i-1)*sizeof(int));
			client_list.num--;
			client_list.maintenance=0;
		}
	}
	return NULL;
}
